-------------------------------------------------------------------------------
-- RTSAI - Real-Time Strategy engine for AI research
-- Copyright(C) 2007 DIMAp-UFRN, licensed under LGPL
--
-- RTSAI "Capture the flag" application
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- GLOBAL VARIABLES
--
seeker_unit = nil
target = rtsai.Vector3D:new ( 0, 0, 0 )

-------------------------------------------------------------------------------
-- Create several obstacles, picking a random center and looping until no
-- overlap with other obstacles and the home base
--
function create_obstacles ( max_obst )

    local requiredClearance = 3
    local defaultRadius = 2
    local objs = {}

    local i = 0
    while i < max_obst do
        -- random radius and center for a new obstacle
        local distance_from_base = math.random ( 8, 38 )
        local random_on_ring = rtsai.OpenSteer.RandomUnitVectorOnXZPlane () * distance_from_base
        local pos = random_on_ring + target

        -- test overlap on previous objects
        local clearanceOk = true
        for i = 1, #objs do
            local d = rtsai.Vector3D:distance ( pos, objs[i] )
            local clearance = d - ( 2 * defaultRadius )
            if clearance < requiredClearance then
                clearanceOk = false
                break
            end
        end
        if clearanceOk then
            i = i + 1
            objs[i] = pos
            game.objects.stone : new ( pos )
        end
    end
    return objs
end


-------------------------------------------------------------------------------
-- Randomize a starting position and heading for a unit
--
function randomize_position ( unit )
    local random_on_ring = rtsai.OpenSteer.RandomUnitVectorOnXZPlane () * 20
    unit : setPosition ( random_on_ring + target )
    unit : randomizeHeadingOnXZPlane ()
end


-------------------------------------------------------------------------------
-- Setup players units surrounding the home base
--
function create_units ( max_obst )

    seeker_unit = game.players.seeker : createUnit ( poti.warrior, seeking_the_flag )
    randomize_position ( seeker_unit )
    --[
    local attacker_units = {}
    attacker_units[1] = game.players.attacker : createUnit ( poti.warrior, attacking )
    attacker_units[2] = game.players.attacker : createUnit ( poti.warrior, attacking )
    attacker_units[3] = game.players.attacker : createUnit ( poti.warrior, attacking )
    attacker_units[4] = game.players.attacker : createUnit ( poti.warrior, attacking )

    randomize_position ( attacker_units[1] )
    randomize_position ( attacker_units[2] )
    randomize_position ( attacker_units[3] )
    randomize_position ( attacker_units[4] )
    --]]
end


--=============================================================================
-- GAME CONFIG
--=============================================================================

Game {
    -- PLAYERS ----------------------------------------------------------------
    players = {
        attacker = { civilization = "poti", strategy = "attacker" },
        seeker = { civilization = "poti", strategy = "seeker" }
    },

    -- ACTIONS ----------------------------------------------------------------
    -- the actions that unit may perform
    actions = {
        move = {
            -- evaluate if there is no obstacle
            precondition = function ( self, unit )
                --[[
                local obst = game:get_objs()
                for i = 0, obst:size()-1 do
                    if unit : predictCollision ( obst[i], self.steer, game.elapsed_time ) then
                        log_predict_collision ( unit, obst[i] )
                        return false
                    end
                end
                --]]
                local obst = game:get_objs()
                local obstacleAvoidance = unit:steerToAvoidObjects ( 2, obst )
                if not isVectorNull ( obstacleAvoidance ) then
                    action.move.steer = obstacleAvoidance
                end
                return true
            end,

            execute = function ( self, unit, elapsed_time )
                --[[
                local obst = game:get_objs()
                local avoid_obst = unit:steerToAvoidObstacles ( 2, obst )
                -- if avoid_obst == rtsai.Vector3D.zero then
                if avoid_obst.x == 0 and avoid_obst.y == 0 and avoid_obst.z == 0 then
                    unit:applySteeringForce ( self.steer, elapsed_time )
                else
                    unit:applySteeringForce ( avoid_obst, elapsed_time )
                end
                --]]
                log_vector ("Steering force", self.steer)
                unit:applySteeringForce ( self.steer, elapsed_time )
            end
        },

        change_state = {
            execute = function ( self, unit, elapsed_time )
                if self.state then
                    unit:change_state ( self.state:new() )
                end
            end
        }
    },


    -- EVENTS -----------------------------------------------------------------
    -- events may happen during the game
    events = {
        flag_captured = function ()
            local dist = rtsai.Vector3D:distance ( seeker_unit:position(), target )
            if dist < seeker_unit:radius() then
                return {}  -- returning something means that the event happened
            end
        end,

        seeker_captured = function ()

        end
    },


    -- OBJECTS ----------------------------------------------------------------
    -- the type of objects in the game. The objects themselves need to be
    -- created in the init function (according to the type defined here).
    objects = {
        stone  = circ (2, true),     -- a circular object with radius 2. It is an obstacle
        target = circ (1.5, false),  -- a circular object with radius 1. It is not an obstacle
    },

    -- AUXILIARY FUNCTIONS ----------------------------------------------------

    -- Game initialization.
    init = function ( self )
        game.objects.target : new ( target )
        create_obstacles ( 15 )
        create_units ()
    end
}
